package cleancode.datasource;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

import cleancode.model.Operation;
import cleancode.model.PersistentData;
import cleancode.model.Role;
import cleancode.model.User;
import cleancode.util.StringUtils;

/**
 * Read data from source file, also can implement other formats of file, such as XML handler, jason handler, etc. This
 * formats is like below: Operation1,Operation2,Operation3...
 * Role1:Operation1,Operation2,Operation3;Role2:Operation1,Operation2,Operation3;...
 * User1:Role1,Role2,Role3;User2:Role1,Role2,Role3;...
 */
public class DefaultFileDataSourceHandler extends DefaultDataSourceHandler
{
    File sourceFile;

    public DefaultFileDataSourceHandler( File fileSource )
    {
        sourceFile = fileSource;
    }

    public void load( PersistentData systemPersistentData ) throws Exception
    {
        if( sourceFile.exists() )
        {
            BufferedReader reader = new BufferedReader( new FileReader( sourceFile ) );
            String line = reader.readLine();
            if( line != null && line.length() > 0 )
            {
                readOperation( line, systemPersistentData );
            }
            line = reader.readLine();
            if( line != null && line.length() > 0 )
            {
                readRole( line, systemPersistentData );
            }
            line = reader.readLine();
            if( line != null && line.length() > 0 )
            {
                readUser( line, systemPersistentData );
            }
            reader.close();
        }
    }

    private void readOperation( String line, PersistentData systemPersistentData )
    {
        for( String operationName : line.split( "," ) )
        {
            systemPersistentData.getOperations().add( new Operation( operationName ) );
        }
    }

    private void readRole( String line, PersistentData systemPersistentData )
    {
        for( String[] roleDefinition : getDefinitionMatrix( line ) )
        {
            Role role = new Role( roleDefinition[0] );
            for( int i = 1; i < roleDefinition.length; i++ )
            {
                Operation operation = systemPersistentData.getOperations().get( roleDefinition[i] );
                role.getSupportedOperations().add(
                    operation != null ? operation : Operation.getBasicOperations().get( roleDefinition[i] ) );
            }
            systemPersistentData.getRoles().add( role );
        }
    }

    private void readUser( String line, PersistentData systemPersistentData )
    {
        for( String[] userDefinition : getDefinitionMatrix( line ) )
        {
            User user = new User( userDefinition[0] );
            for( int i = 1; i < userDefinition.length; i++ )
            {
                user.getRoles().add( systemPersistentData.getRoles().get( userDefinition[i] ) );
            }
            systemPersistentData.getUsers().add( user );
        }
    }

    /**
     * Change a:b,c,d;e:f,g to [a,b,c,d][e,f,g]
     * 
     * @param line formatted line
     * @return matrix
     */
    private String[][] getDefinitionMatrix( String line )
    {
        String[] definitionGroups = line.replace( ':', ',' ).split( ";" );
        String[][] definitionMatrix = new String[ definitionGroups.length ][];
        for( int i = 0; i < definitionGroups.length; i++ )
        {
            definitionMatrix[i] = definitionGroups[i].split( "," );
        }
        return definitionMatrix;
    }

    public void save( PersistentData systemStoredData ) throws Exception
    {
        PrintWriter writer = new PrintWriter( sourceFile );

        writer.println( StringUtils.combineModelNamesWithComma( systemStoredData.getOperations() ) );

        List<String> models = new ArrayList<String>();
        for( Role role : systemStoredData.getRoles() )
        {
            models.add( role.getName() + ":" + StringUtils.combineModelNamesWithComma( role.getSupportedOperations() ) );
        }
        writer.println( StringUtils.combineCollectionsWithSemi( models ) );

        models.clear();
        for( User user : systemStoredData.getUsers() )
        {
            models.add( user.getName() + ":" + StringUtils.combineModelNamesWithComma( user.getRoles() ) );
        }
        writer.print( StringUtils.combineCollectionsWithSemi( models ) );

        writer.close();
    }
}
